//
//  YuanXinBaiduMap.m
//  YuanXinBaiduMap
//
//  Created by 晏德智 on 16/9/26.
//  Copyright © 2016年 晏德智. All rights reserved.
//

#import "RCTBaiduMapViewManager.h"
#import "RCTConvert+BaiduMapKit.h"
#import "RCTBaiduMapView.h"
#import "RCTBaiduMapAnnotation.h"
#import "RCTBaiduMapOverlay.h"
#import "BaiduLocationOptions.h"
#import <BaiduMapAPI_Utils/BMKGeometry.h>
#import <BMKLocationkit/BMKLocationAuth.h>
#import "BaiduMapSdkManager.h"

static NSString *const RCTBaiduMapPinRed = @"#ff3b30";
static NSString *const RCTBaiduMapPinGreen = @"#4cd964";
static NSString *const RCTBaiduMapPinPurple = @"#c969e0";

@implementation RCTConvert (BMKPinAnnotationColor)

RCT_ENUM_CONVERTER(BMKPinAnnotationColor, (@{
                                             RCTBaiduMapPinRed: @(BMKPinAnnotationColorRed),
                                             RCTBaiduMapPinGreen: @(BMKPinAnnotationColorGreen),
                                             RCTBaiduMapPinPurple: @(BMKPinAnnotationColorPurple)
                                             }), BMKPinAnnotationColorRed, unsignedIntegerValue)

@end

@interface RCTBaiduMapViewManager () <BMKMapViewDelegate, BMKGeneralDelegate, BMKLocationAuthDelegate> {
    CLLocationCoordinate2D _previousCenter;
    NSMutableArray<UIView *> *_managedViews;
}

@end

@implementation RCTBaiduMapViewManager

RCT_EXPORT_MODULE()

- (dispatch_queue_t)methodQueue
{
    return dispatch_get_main_queue();
}

- (UIView *)view
{
    [[BaiduMapSdkManager shareInstance] checkPermision];
    RCTBaiduMapView *mapView = [[RCTBaiduMapView alloc] init];
    // RCTBaiduMapView 自己有实现代理，注释掉 mapView.delegate = self，则它自己处理
    mapView.delegate = self;
    mapView.shouldEmitRegionChange = YES;
    __weak RCTBaiduMapView *weakMapView = mapView;
    mapView.onFirstUserLocated = ^(BMKUserLocation *userLocation) {
        //weakMapView.region = BMKCoordinateRegionMake(userLocation.location.coordinate, weakMapView.region.span);
        [self _emitRegionChangeEvent:weakMapView withLocation:userLocation continuous:NO];
    };
    return mapView;
}

-(void)setShowZoomControls:(BOOL)showZoomControls forView:(RCTBaiduMapView *)view {
    [view setShowsZoomControl:showZoomControls];
}

RCT_EXPORT_VIEW_PROPERTY(gesturesEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsUserLocation, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showMapScaleBar, BOOL)
RCT_EXPORT_VIEW_PROPERTY(showsPointsOfInterest, BOOL)
RCT_EXPORT_VIEW_PROPERTY(userTrackingMode, int)

//RCT_EXPORT_VIEW_PROPERTY(followUserLocation, BOOL)
RCT_EXPORT_VIEW_PROPERTY(autoZoomToSpan, BOOL)
RCT_EXPORT_VIEW_PROPERTY(zoomEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(rotateEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(scrollEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(maxDelta, float)
RCT_EXPORT_VIEW_PROPERTY(minDelta, float)
//放大缩小例
RCT_EXPORT_VIEW_PROPERTY(zoomLevel, float)
//设定地图是否打开路况图层
RCT_EXPORT_VIEW_PROPERTY(trafficEnabled, BOOL)
//设定地图是否现显示3D楼块效果
RCT_EXPORT_VIEW_PROPERTY(buildingsEnabled, BOOL)

//设定地图是否打开百度城市热力图图层（百度自有数据）,注：地图层级大于11时，可显示热力图
RCT_EXPORT_VIEW_PROPERTY(baiduHeatMapEnabled, BOOL)
//设定地图View能否支持用户缩放(双击或双指单击)
RCT_EXPORT_VIEW_PROPERTY(zoomEnabledWithTap, BOOL)
RCT_EXPORT_VIEW_PROPERTY(legalLabelInsets, UIEdgeInsets)
//可设定为标准地图、卫星地图
RCT_EXPORT_VIEW_PROPERTY(mapType, int)
RCT_EXPORT_VIEW_PROPERTY(annotations, NSArray<RCTBaiduMapAnnotation *>)
RCT_EXPORT_VIEW_PROPERTY(overlays, NSArray<RCTBaiduMapOverlay *>)
RCT_EXPORT_VIEW_PROPERTY(onAnnotationDragStateChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onAnnotationFocus, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onAnnotationBlur, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onChange, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onDidChangeUserLocation, RCTBubblingEventBlock)
//返回定位坐标点是否在当前地图可视区域内
//RCT_EXPORT_VIEW_PROPERTY(userLocationVisible, BOOL)

RCT_CUSTOM_VIEW_PROPERTY(showsZoomControl, BOOL, RCTBaiduMapView) {
    [self setShowZoomControls:[RCTConvert BOOL:json] forView:view];
}

RCT_CUSTOM_VIEW_PROPERTY(mapCenter, CLLocationCoordinate2D, RCTBaiduMapView) {
    CLLocationCoordinate2D centerCoordinate = json ? [RCTConvert CLLocationCoordinate2D:json] : defaultView.centerCoordinate;
    if (_previousCenter.latitude == centerCoordinate.latitude && _previousCenter.longitude == centerCoordinate.longitude) {
        return; // 避免js重复设置造成死循环。。
    }
    _previousCenter = centerCoordinate;
    double threshold = 0.0001;
    if (fabs(centerCoordinate.latitude - view.centerCoordinate.latitude) > threshold || fabs(centerCoordinate.longitude - view.centerCoordinate.longitude) > threshold) {
        [view setCenterCoordinate:centerCoordinate];
    }
}

RCT_CUSTOM_VIEW_PROPERTY(region, BMKCoordinateRegion, RCTBaiduMapView)
{
    [view setRegion:json ? [RCTConvert BMKCoordinateRegion:json] : defaultView.region animated:YES];
}

RCT_CUSTOM_VIEW_PROPERTY(userLocationViewParams, BMKLocationViewDisplayParam, RCTBaiduMapView)
{
    if (json) {
        [view setUserLocationViewParams:[RCTConvert RCTBaiduMapLocationViewDisplayParam:json]];
    }
}
/*
 这个Conter太难写了。
 RCT_CUSTOM_VIEW_PROPERTY(userLocationData, BMKUserLocation, RCTBaiduMapView)
 {
 if (json) {
 //[view userLocationData:[RCTConvert BM:json]];
 }
 }
 */

- (NSDictionary<NSString *, id> *)constantsToExport
{
    NSString *red, *green, *purple;
    
    red = RCTBaiduMapPinRed;
    green = RCTBaiduMapPinGreen;
    purple = RCTBaiduMapPinPurple;
    
    return @{
             @"PinColors": @{
                     @"RED": red,
                     @"GREEN": green,
                     @"PURPLE": purple,
                     }
             };
    
}

RCT_EXPORT_METHOD(startUserLocation:(nonnull NSNumber *)reactTag  locationOptions:(BaiduLocationOptions)options){
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
         RCTBaiduMapView *view = (RCTBaiduMapView *)viewRegistry[reactTag];
         if (!view || ![view isKindOfClass:[RCTBaiduMapView class]]) {
             RCTLogError(@"Cannot find RCTBaiduMap with tag #%@", reactTag);
             return;
         }
         [view startUserLocation:options];
     }];
    
}

RCT_EXPORT_METHOD(stopUserLocation:(nonnull NSNumber *)reactTag){
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
         RCTBaiduMapView *view = (RCTBaiduMapView *)viewRegistry[reactTag];
         if (!view || ![view isKindOfClass:[RCTBaiduMapView class]]) {
             RCTLogError(@"Cannot find RCTBaiduMap with tag #%@", reactTag);
             return;
         }
         [view stopUserLocation];
     }];
}

RCT_EXPORT_METHOD(addPointAnnotation:(nonnull NSNumber *)reactTag pointAnnotation:(RCTBaiduMapAnnotation *)pointAnnotation){
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
         RCTBaiduMapView *view = (RCTBaiduMapView *)viewRegistry[reactTag];
         if (!view || ![view isKindOfClass:[RCTBaiduMapView class]]) {
             RCTLogError(@"Cannot find RCTBaiduMap with tag #%@", reactTag);
             return;
         }
         
         if (pointAnnotation) {
             [view addAnnotation:pointAnnotation];
         }
     }];
    
}

RCT_EXPORT_METHOD(updatePointAnnotation:(nonnull NSNumber *)reactTag pointAnnotation:(RCTBaiduMapAnnotation *)pointAnnotation){
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
         RCTBaiduMapView *view = (RCTBaiduMapView *)viewRegistry[reactTag];
         if (!view || ![view isKindOfClass:[RCTBaiduMapView class]]) {
             RCTLogError(@"Cannot find RCTBaiduMap with tag #%@", reactTag);
             return;
         }
         
         if (pointAnnotation) {
             RCTBaiduMapAnnotation *oldAnnotion = nil;
             for (RCTBaiduMapAnnotation *item in view.annotations) {
                 if([item.identifier compare:pointAnnotation.identifier options:NSCaseInsensitiveSearch]== NSOrderedSame){
                     oldAnnotion = item;
                 }
             }
             if(oldAnnotion){
                 [view removeAnnotation:oldAnnotion];
             }
             [view addAnnotation:pointAnnotation];
         }
         
     }];
    
}


RCT_EXPORT_METHOD(chanageMapStates:(nonnull NSNumber *)reactTag mapStatus:(BMKMapStatus *)state){
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
         RCTBaiduMapView *view = (RCTBaiduMapView *)viewRegistry[reactTag];
         if (!view || ![view isKindOfClass:[RCTBaiduMapView class]]) {
             RCTLogError(@"Cannot find RCTBaiduMap with tag #%@", reactTag);
             return;
         }
         [view setMapStatus:state withAnimation:YES];
     }];
}

RCT_EXPORT_METHOD(zoomToLocs:(nonnull NSNumber *)reactTag
                  locations:(NSArray<CLLocation *> *)locs
                  isRegionChange:(id)isRegionChange)
{
    [self.bridge.uiManager addUIBlock:
     ^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry){
         RCTBaiduMapView *view = (RCTBaiduMapView *)viewRegistry[reactTag];
         if (!view || ![view isKindOfClass:[RCTBaiduMapView class]]) {
             RCTLogError(@"Cannot find RCTBaiduMap with tag #%@", reactTag);
             return;
         }
         /*
         NSMutableArray<CLLocation *> *resultPoints = [NSMutableArray new];
         if (locs != nil && locs.count > 0) {
             for (NSDictionary *oneLocation in locs) {
                 
                 // NSDictionary *oneLocation = (NSDictionary *)item;
                 if ([oneLocation objectForKey:@"latitude"] && [oneLocation objectForKey:@"longitude"]) {
                     double latitude = [[oneLocation objectForKey:@"latitude"] doubleValue];
                     double longitude = [[oneLocation objectForKey:@"longitude"] doubleValue];
                     [resultPoints addObject:[[CLLocation alloc] initWithLatitude:latitude longitude:longitude]];
                 }
                 
             }
         }
           [view zoomToSpan:resultPoints];
          */
         view.shouldEmitRegionChange = [RCTConvert BOOL:isRegionChange];
         [view zoomToSpan:locs];
     }];
}

#pragma mark - BMKMapViewDelegate

- (void)mapView:(RCTBaiduMapView *)mapView didSelectAnnotationView:(BMKAnnotationView *)view
{
    if (mapView.onPress && [view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        mapView.onPress(@{
                          @"action": @"annotation-click",
                          @"annotation": @{
                                  @"id": annotation.identifier,
                                  @"title": annotation.title ?: @"",
                                  @"subtitle": annotation.subtitle ?: @"",
                                  @"latitude": @(annotation.coordinate.latitude),
                                  @"longitude": @(annotation.coordinate.longitude)
                                  }
                          });
    }
    
    if ([view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        if (mapView.onAnnotationFocus) {
            mapView.onAnnotationFocus(@{
                                        @"annotationId": annotation.identifier
                                        });
        }
    }
}

- (void)mapView:(RCTBaiduMapView *)mapView didDeselectAnnotationView:(BMKAnnotationView *)view
{
    if ([view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        if (mapView.onAnnotationBlur) {
            mapView.onAnnotationBlur(@{
                                       @"annotationId": annotation.identifier
                                       });
        }
    }
}

- (void)mapView:(RCTBaiduMapView *)mapView annotationView:(BMKAnnotationView *)view didChangeDragState:(BMKAnnotationViewDragState)newState fromOldState:(BMKAnnotationViewDragState)oldState
{
    static NSArray *states;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        states = @[@"idle", @"starting", @"dragging", @"canceling", @"ending"];
    });
    
    if ([view.annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        RCTBaiduMapAnnotation *annotation = (RCTBaiduMapAnnotation *)view.annotation;
        if (mapView.onAnnotationDragStateChange) {
            mapView.onAnnotationDragStateChange(@{
                                                  @"state": states[newState],
                                                  @"oldState": states[oldState],
                                                  @"annotationId": annotation.identifier,
                                                  @"latitude": @(annotation.coordinate.latitude),
                                                  @"longitude": @(annotation.coordinate.longitude),
                                                  });
        }
    }
}

- (BMKAnnotationView *)mapView:(RCTBaiduMapView *)mapView viewForAnnotation:(RCTBaiduMapAnnotation *)annotation
{
    if (![annotation isKindOfClass:[RCTBaiduMapAnnotation class]]) {
        return nil;
    }
    
    BMKAnnotationView *annotationView;
    if (annotation.viewIndex != NSNotFound) {
        NSString *reuseIdentifier = NSStringFromClass([RCTBaiduMapAnnotationView class]);
        annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
        if (!annotationView) {
            annotationView = [[RCTBaiduMapAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
        }
        
        UIView *reactView = (UIView *)mapView.reactSubviews[annotation.viewIndex];
        ((RCTBaiduMapAnnotationView *)annotationView).contentView = reactView;
    }/* else if (annotation.image) {
      NSString *reuseIdentifier = NSStringFromClass([BMKAnnotationView class]);
      annotationView = [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier];
      if (!annotationView) {
      annotationView = [[BMKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:reuseIdentifier];
      }
      annotationView.image = annotation.image;
      annotationView.centerOffset = CGPointMake(0, 0 - annotationView.image.size.height / 2);
      }*/ else {
          
          NSString *reuseIdentifier = NSStringFromClass([BMKPinAnnotationView class]);
          annotationView =
          [mapView dequeueReusableAnnotationViewWithIdentifier:reuseIdentifier] ?:
          [[BMKPinAnnotationView alloc] initWithAnnotation:annotation
                                           reuseIdentifier:reuseIdentifier];
          ((BMKPinAnnotationView *)annotationView).animatesDrop = annotation.animateDrop;
          
          //        ((BMKPinAnnotationView *)annotationView).pinColor = annotation.tintColor;
          
          
      }
    
    annotationView.canShowCallout = (annotation.title.length > 0);
    
    // 这个回调会早于 insertReactSubview 调用。。只要调用 addAnnotation 或 addAnnotations 方法就会触发
    // 所以为了解决这个问题，暂时先在每次调用 insertReactSubview 时，更新下，使再次触发这个回调
    if (annotation.leftCalloutViewIndex != NSNotFound) {
        NSArray<UIView *> *reactSubviews = mapView.reactSubviews;
        if (reactSubviews.count > annotation.leftCalloutViewIndex) {
            annotationView.leftCalloutAccessoryView = reactSubviews[annotation.leftCalloutViewIndex];
        }
    } else if (annotation.hasLeftCallout) {
        annotationView.leftCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    } else {
        annotationView.leftCalloutAccessoryView = nil;
    }
    
    if (annotation.rightCalloutViewIndex != NSNotFound) {
        NSArray<UIView *> *reactSubviews = mapView.reactSubviews;
        if (reactSubviews.count > annotation.rightCalloutViewIndex) {
            annotationView.rightCalloutAccessoryView = reactSubviews[annotation.rightCalloutViewIndex];
        }
    } else if (annotation.hasRightCallout) {
        annotationView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];
    } else {
        annotationView.rightCalloutAccessoryView = nil;
    }
    
    annotationView.draggable = annotation.draggable;
    return annotationView;
}

- (BMKOverlayView *)mapView:(BMKMapView *)mapView viewForOverlay:(RCTBaiduMapOverlay *)overlay
{
    if ([overlay isKindOfClass:[RCTBaiduMapOverlay class]]) {
        BMKPolylineView *polylineView = [[BMKPolylineView alloc] initWithOverlay:overlay];
        polylineView.strokeColor = overlay.strokeColor;
        polylineView.lineWidth = overlay.lineWidth;
        return polylineView;
    }
    
    return nil;
}

- (void)mapView:(RCTBaiduMapView *)mapView regionWillChangeAnimated:(BOOL)animated
{
    [self _regionChanged:mapView];
    mapView.regionChangeObserveTimer = [NSTimer timerWithTimeInterval:RCTBaiduMapRegionChangeObserveInterval target:self selector:@selector(_onTick:) userInfo:@{RCTBaiduMapViewKey: mapView} repeats:YES];
    
    [[NSRunLoop mainRunLoop] addTimer:mapView.regionChangeObserveTimer forMode:NSRunLoopCommonModes];
}

- (void)mapView:(RCTBaiduMapView *)mapView regionDidChangeAnimated:(BOOL)animated
{
    [mapView.regionChangeObserveTimer invalidate];
    mapView.regionChangeObserveTimer = nil;
    
    [self _regionChanged:mapView];
    
    if (mapView.hasStartedRendering) {
        if (mapView.shouldEmitRegionChange) {
            [self _emitRegionChangeEvent:mapView continuous:NO];
        } else {
            mapView.shouldEmitRegionChange = YES; // 恢复正常状态
        }
    }
}

- (void)mapViewDidFinishLoading:(RCTBaiduMapView *)mapView
{
    mapView.hasStartedRendering = YES;
    if (mapView.autoZoomToSpan) {
        [mapView zoomToSpan];
    }
}

#pragma mark - Private

- (void)_onTick:(NSTimer *)timer
{
    [self _regionChanged:timer.userInfo[RCTBaiduMapViewKey]];
}

- (void)_regionChanged:(RCTBaiduMapView *)mapView
{
    BOOL needZoom = NO;
    CGFloat newLongitudeDelta = 0.0f;
    BMKCoordinateRegion region = mapView.region;
    
    if (!CLLocationCoordinate2DIsValid(region.center)) {
        return;
    }
    
    if (mapView.maxDelta > FLT_EPSILON && region.span.longitudeDelta > mapView.maxDelta) {
        needZoom = YES;
        newLongitudeDelta = mapView.maxDelta * (1 - RCTBaiduMapZoomBoundBuffer);
    } else if (mapView.minDelta > FLT_EPSILON && region.span.longitudeDelta < mapView.minDelta) {
        needZoom = YES;
        newLongitudeDelta = mapView.minDelta * (1 + RCTBaiduMapZoomBoundBuffer);
    }
    if (needZoom) {
        region.span.latitudeDelta = region.span.latitudeDelta / region.span.longitudeDelta * newLongitudeDelta;
        region.span.longitudeDelta = newLongitudeDelta;
        mapView.region = region;
    }
    
    [self _emitRegionChangeEvent:mapView continuous:YES];
}

- (void)_emitRegionChangeEvent:(RCTBaiduMapView *)mapView continuous:(BOOL)continuous
{
    if (mapView.onChange) {
        BMKCoordinateRegion region = mapView.region;
        if (!CLLocationCoordinate2DIsValid(region.center)) {
            return;
        }
        
        mapView.onChange(@{
                           @"continuous": @(continuous),
                           @"region": @{
                                   @"latitude": @(RCTZeroIfNaN(region.center.latitude)),
                                   @"longitude": @(RCTZeroIfNaN(region.center.longitude)),
                                   @"latitudeDelta": @(RCTZeroIfNaN(region.span.latitudeDelta)),
                                   @"longitudeDelta": @(RCTZeroIfNaN(region.span.longitudeDelta)),
                                   }
                           });
    }
}

- (void)_emitRegionChangeEvent:(RCTBaiduMapView *)mapView withLocation:(BMKUserLocation *)userLocation continuous:(BOOL)continuous
{
    if (mapView.onChange) {
        BMKCoordinateRegion region = mapView.region;
        if (!CLLocationCoordinate2DIsValid(userLocation.location.coordinate)) {
            return;
        }
        
        mapView.onChange(@{
                           @"continuous": @(continuous),
                           @"region": @{
                                   @"latitude": @(RCTZeroIfNaN(userLocation.location.coordinate.latitude)),
                                   @"longitude": @(RCTZeroIfNaN(userLocation.location.coordinate.longitude)),
                                   @"latitudeDelta": @(RCTZeroIfNaN(region.span.latitudeDelta)),
                                   @"longitudeDelta": @(RCTZeroIfNaN(region.span.longitudeDelta)),
                                   }
                           });
    }
}

@end
